using System;
using System.Collections.Generic;
using System.Linq;
using Sustainalytics.ESGRatings.Computing.Entities;
using Sustainalytics.ESGRatings.Computing.Entities.ESG;
using Sustainalytics.Utils;

namespace Sustainalytics.ESGRatings.BusinessLogic
{

    public class TemporaryDataSetBuilder
    {
        private readonly HashSet<string> _indicatorCodes = new HashSet<string>();

        public TemporaryDataSetBuilder()
        {
            Companies = new Dictionary<Guid, CompanyData>();
            CompaniesRawScores = new Dictionary<Guid, CompanyScores>();
            IndicatorTemplates = new List<IndicatorTemplate>();
            Matrices = new Dictionary<Guid, WeightMatrix>();
            ReferenceUniverses = new Dictionary<Guid, ReferenceUniverse>();
        }

        private Dictionary<Guid, CompanyData> Companies { get; set; }

        private Dictionary<Guid, CompanyScores> CompaniesRawScores { get; set; }

        private List<IndicatorTemplate> IndicatorTemplates { get; set; }

        private Dictionary<Guid, WeightMatrix> Matrices { get; set; }

        private Dictionary<Guid, ReferenceUniverse> ReferenceUniverses { get; set; }

        private Guid? DefaultWeightMatrix;

        public void SetDefaultWeightMatrix(Guid defaultMatrixId)
        {
            DefaultWeightMatrix = defaultMatrixId;
        }

        public bool AddCompanies(ICollection<CompanyData> companies)
        {
            if (companies.IsNullOrEmpty())
                return false;
            foreach (var company in companies.Where(company => !Companies.ContainsKey(company.Id)))
            {
                Companies.Add(company.Id, company);
            }
            return true;
        }

        public bool AddCompaniesRawScores(ICollection<CompanyScores> companiesRawScores)
        {
            if (companiesRawScores.IsNullOrEmpty())
                return false;
            foreach (var companyRawScores in companiesRawScores.Where(x => !CompaniesRawScores.ContainsKey(x.CompanyId)))
            {
                CompaniesRawScores.Add(companyRawScores.CompanyId, companyRawScores);
            }
            return true;
        }

        public bool AddCompanyWeights(Guid matrixId, Guid? accountId, ESG.Entities.CompanyWeights weightMatrixRow, bool profileIsValid)
        {
            if (weightMatrixRow.Weights.IsNullOrEmpty())
            {
                return false;
            }

            var adaptedWeight = new CompanyWeights(weightMatrixRow.CompanyId, weightMatrixRow.Weights.Select(w => new Weight(w.Key, w.Value)), profileIsValid);

            if (Matrices.ContainsKey(matrixId))
            {
                this.AddWeightsToMatrix(matrixId, adaptedWeight);
            }
            else
            {
                Matrices.Add(matrixId, new WeightMatrix(accountId, matrixId, adaptedWeight));
            }

            this.AddMatrixIndicatorsToIndicatorsSet(new List<CompanyWeights>() { adaptedWeight });
            return true;
        }

        public bool AddIndicatorTemplates(ICollection<IndicatorTemplate> templates)
        {
            if (templates.IsNullOrEmpty())
            {
                return false;
            }

            IndicatorTemplates.AddRange(templates);
            return true;
        }

        public bool AddMatrices(ICollection<WeightMatrix> matrices)
        {
            if (matrices.IsNullOrEmpty())
            {
                return false;
            }

            foreach (var matrix in matrices.Where(x => !Matrices.ContainsKey(x.MatrixId)))
            {
                Matrices.Add(matrix.MatrixId, matrix);
                AddMatrixIndicatorsToIndicatorsSet(matrix.CompaniesWeights);
            }

            return true;
        }

        public bool AddReferenceUniverses(ICollection<ReferenceUniverse> universes)
        {
            if (universes.IsNullOrEmpty())
            {
                return false;
            }
            foreach (var universe in universes.Where(x => !ReferenceUniverses.ContainsKey(x.Id)))
            {
                ReferenceUniverses.Add(universe.Id, universe);
            }

            return true;
        }

        public TemporaryDataSet BuilDataSet()
        {
            return BuildDataSet(Guid.NewGuid(), DateTime.UtcNow);
        }

        public TemporaryDataSet BuildDataSet(Guid id, DateTime date)
        {
            var result = new TemporaryDataSet(id, date);
            result.Companies = Companies.Values.ToArray();
            result.Matrices = Matrices.Values.ToArray();
            result.CompaniesRawScores = FilteredCompaniesRawScores();
            result.IndicatorTemplates = FilterIndicatorTemplates();
            result.ReferenceUniverses = ReferenceUniverses.Values.ToArray();
            result.DefaultWeightMatrix = DefaultWeightMatrix;
            return result;
        }

        private void AddMatrixIndicatorsToIndicatorsSet(ICollection<CompanyWeights> weights)
        {
            foreach (var indicator in
                weights.SelectMany(x => x.IndicatorsData)
                    .Where(indicator => !this._indicatorCodes.Contains(indicator.Code)))
            {
                this._indicatorCodes.Add(indicator.Code);
            }
        }

        private void AddWeightsToMatrix(Guid matrixId, CompanyWeights weights)
        {
            var items = this.Matrices[matrixId].CompaniesWeights.ToList();
            items.Add(weights);
            this.Matrices[matrixId].CompaniesWeights = items.ToArray();
        }

        private CompanyScores[] FilteredCompaniesRawScores()
        {
            var result = new List<CompanyScores>();
            foreach (var companyScores in CompaniesRawScores)
            {
                result.Add(companyScores.Value);
            }
            return result.ToArray();
        }

        private IndicatorTemplate[] FilterIndicatorTemplates()
        {
            if (_indicatorCodes.IsNullOrEmpty())
                return IndicatorTemplates.ToArray();

            var result = IndicatorTemplates.Where(it => _indicatorCodes.Contains(it.Code));
            return result.ToArray();
        }

       
    }
}